package com.lin.manager.redis.service;

import com.lin.manager.redis.constants.RedisConstants;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.data.redis.core.RedisTemplate;

import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * @作者: 林江
 * @创建时间: 2024/3/18
 * @功能:
 */
public class RedisService {
    RedisTemplate redis;
    RedissonClient redisson;

    public RedisService(RedisTemplate redis) {
        this.redis = redis;
    }

    public void setRedisson(RedissonClient redisson) {
        this.redisson = redisson;
    }

    /*-----------------------String-------------------------------*/
    public <K, V> void setStr(K k, V v) {
        this.setExpStr(k, v, -1L, null);
    }

    public <K, V> void setDayStr(K k, V v, long time) {
        this.setExpStr(k, v, time, TimeUnit.DAYS);
    }

    public <K, V> void setExpStr(K k, V v, long time, TimeUnit unit) {
        redis.boundValueOps(k).set(v, time, unit);
    }

    public <K> String getStr(K k) {
        if (exist(k)) {
            return (String) redis.opsForValue().get(k);
        }
        return "";
    }

    /*------------------------------Hash------------------------------------*/
    public <HK, K, V> void setHash(HK hk, K k, V v) {
        this.setExpHash(hk, k, v, -1L, null);
    }

    public <HK, V> void setHash(HK hk, Map<?, V> map) {
        this.setExpHashs(hk, map, -1L, null);
    }

    public <HK, K, V> void setDayHash(HK hk, K k, V v, long time) {
        this.setExpHash(hk, k, v, time, TimeUnit.DAYS);
    }

    public <HK, K, V> void setExpHash(HK hk, K k, V v, long time, TimeUnit unit) {
        redis.boundHashOps(hk).put(k, v);
        this.expire(hk, time, unit);
    }

    public <HK, V> void setExpHashs(HK hk, Map<?, V> map, long time, TimeUnit unit) {
        redis.boundHashOps(hk).putAll(map);
        this.expire(hk, time, unit);
    }

    public <HK, K, T> T getHash(HK hk, K k, Class<T> c) {
        if (exist(hk)) {
            return (T) redis.opsForHash().get(hk, k);
        }
        return null;
    }

    public <HK, T> Map<String, T> getHashValues(HK hk, Class<T> c) {
        if (exist(hk)) {
            return (Map<String, T>) redis.opsForHash().values(hk);
        }
        return null;
    }

    public <HK, K> void removeHash(HK hk, K k) {
        if (exist(hk)) {
            redis.boundHashOps(hk).delete(k);
        }
    }

    /*-------------------------------其他----------------------------*/
    public <K> void expire(K k, long time, TimeUnit unit) {
        redis.expire(k, time, unit);
    }

    public <K> boolean exist(K k) {
        return redis.hasKey(k);
    }

    public <K> void delete(K k) {
        redis.delete(k);
    }

    /*-----------------------------redis锁---------------------------------*/

    /**
     * 多线程情况下，线程阻塞
     *
     * @return
     */
    public <K, T> T executeLock(RedissonOperate<T> operate, String hk, K k, Class<T> returType) {
        String lockName = hk.concat(k.toString());
        RLock lock = redisson.getLock(lockName);
        try {
            lock.lock(); // 上锁
            return execute(operate, RedisConstants.REDISSON.concat(hk), k, returType);
        } finally {
            lock.unlock(); // 解锁
        }
    }

    /**
     * 多线程情况下，线程不阻塞
     *
     * @return
     */
    public <K, T> T executeTryLock(RedissonOperate<T> operate, String hk, K k, Class<T> returType) {
        String lockName = hk.concat(k.toString());
        RLock lock = redisson.getLock(lockName);
        /**
         * 尝试获取锁,没有获取到锁，立即返回
         */
        if (lock.tryLock()) {
            try {
                return execute(operate, RedisConstants.REDISSON.concat(hk), k, returType);
            } finally {
                lock.unlock(); // 解锁
            }
        } else {
            throw new RuntimeException("缓存数据没有准备好");
        }
    }

    private <K, T> T execute(RedissonOperate<T> operate, String hk, K k, Class<T> returType) {
        // 从缓存获取数据
        T hashValue = this.getHash(hk, k, returType);
        if (Objects.nonNull(hashValue)) {
            return hashValue;
        }

        // 执行具体的业务
        T data = operate.execute();

        // 缓存数据
        if (Objects.nonNull(data)) {
            this.setExpHash(hk, k, data, 1, TimeUnit.DAYS);
        }
        return data;
    }
}
